import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.StringTokenizer;

public class HTTPHandler extends Thread {
    
    // Socket used by the HTTP Handler
    private Socket socket = null;
    
    // Input pipe to the socket
    private BufferedReader input = null;
    
    // Output pipe out of the socket
    private PrintWriter	output = null;
    
    // Size of the chunk of data read in each iteration
    public static int DEFAULT_CHUNK_SIZE = 1024;
    
    // Flag used to stop all of the HTTP Handlers
    public static boolean stop = false;
    
    /**
     * HTTP Handler constructor
     * @param connection Connection used for the HTTP Handler
     */
    public HTTPHandler(Socket connection) {
        socket = connection;
    }
    
    /**
     * Reads client HTTP requests and returns them in a pre-processed format
     *
     * @return Pre-processed HTTP requests
     */
    public void readRequests() {
        // Receive requests
        try {
            String line;
            // If the HTTP Handler is allowed to run, the socket exists, is connected and a line is read from the socket
            while (!HTTPHandler.stop && socket != null && socket.isConnected() && (line = input.readLine()) != null)
                answerRequest(line);
        } catch (IOException e1) {
            System.err.println("ERROR: Problem receiving client input.");
        }
    }
    
    /**
     * Sends replies to the HTTP client according to the given list of requests
     *
     * @param list_requests
     *            Pre-processed HTTP requests
     */
    public void answerRequest(String request) {
        System.out.println(request);
        StringTokenizer tokenizer = new StringTokenizer(request, " ");
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            if (token.equals("GET")) {
                String url = tokenizer.nextToken();
                MainWindow.window.addLogEntry("URL request received: " + url);
                sendFile(url);
            }
        }
    }
    
    /**
     * Sends a requested file to the browser
     *
     * @param url
     */
    public void sendFile(String url) {
        long size = 0;
        // Remove the first slash
        String pre_url = url.substring(1);
        
        // If there is nothing left, then it was a request for the default page
        if (pre_url.length() == 0)
            pre_url = MainWindow.DEFAULT_INDEX_FILE;
        try {
            File file = new File(MainWindow.SERVER_ROOT + pre_url);
            size = file.length();
            DataInputStream file_reader = new DataInputStream(new FileInputStream(MainWindow.SERVER_ROOT + pre_url));
            output.println("HTTP/1.0 200 OK");
            output.println("Content-Length: " + size);
            output.println("");
            byte[] buffer = new byte[HTTPHandler.DEFAULT_CHUNK_SIZE];
            while (file_reader.read(buffer) != -1) {
                try {
                    DataOutputStream binary_output = new DataOutputStream(socket.getOutputStream());
                    binary_output.write(buffer, 0, (int) DEFAULT_CHUNK_SIZE);
                } catch (IOException e) {
                    System.err.println("ERROR: Problem sending requested file.");
                }
                buffer = new byte[HTTPHandler.DEFAULT_CHUNK_SIZE];
            }
            
            MainWindow.window.addLogEntry("File sent: " + url);
        } catch (FileNotFoundException e) {
            output.println("HTTP/1.0 404 Not Found");
            String response = new String("<html><head><title>FILE NOT FOUND!</title></head><body><h1>FILE NOT FOUND: "+ MainWindow.SERVER_ROOT + pre_url +"</h1></body></html>");
            output.println("Content-Length: " + response.length());
            output.println("");
            output.println(response);
            MainWindow.window.addLogEntry("File not found: " + url);
            System.err.println("ERROR: Requested file was not found on server storage.");
        } catch (IOException e) {
            MainWindow.window.addLogEntry("File not found: " + url);
            System.err.println("ERROR: Problem reading requested file.");
        }
    }
    
    public void run() {
        MainWindow.window.addLogEntry("New HTTP Handler started");
        
        do {
            while(socket == null)
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            
            MainWindow.window.addActiveThread();
            
            // Attach socket buffers
            try {
                input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                output = new PrintWriter(socket.getOutputStream(), true);
            } catch (IOException e) {
                System.err.println("ERROR: Problem attaching socket buffers.");
            }
            
            // Reply
            readRequests();
            
            // Terminate
            try {
                input.close();
                output.close();
                socket.close();
                socket = null;
            } catch (IOException e) {
                System.err.println("ERROR: Problem whilst shutting down connection.");
                socket = null;
            } finally {
                socket = null;
            }
            
            MainWindow.window.removeActiveThread();
            
            if(MainWindow.available_threads.size() < MainWindow.MINIMUM_CONNECTION_POOL && MainWindow.CONNECTION_HANDLING_TYPE == 3)
            {
                MainWindow.available_threads.add(this);
                MainWindow.window.addSleepingThread();
            }
        }
        while(!stop && MainWindow.CONNECTION_HANDLING_TYPE == 3);
    }
    
    public void setSocket(Socket socket) {
        this.socket = socket;
    }
}
